load(":editorconfig.bzl", "get_editorconfig", "is_android_rules_enabled", "is_experimental_rules_enabled")
load(":ktlint_config.bzl", "KtlintConfigInfo")

def _ktlint_fix_impl(ctx):
    editorconfig = get_editorconfig(ctx.attr.config)

    args = ["--format"]
    if editorconfig:
        args.append("--editorconfig={file}".format(file = editorconfig.path))
    if is_android_rules_enabled(ctx.attr.config):
        args.append("--android")
    if is_experimental_rules_enabled(ctx.attr.config):
        args.append("--experimental")
    args.append("--relative")

    # Much of the following is lifted from:
    # https://cs.opensource.google/bazel/bazel/+/refs/tags/4.0.0:src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt;l=114
    content = """#!/bin/bash
# Find our runfiles tree.
#
# Call this program X.  X was generated by a rule.
# X may be invoked in many ways:
#   1a) directly by a user, with $0 in the output tree
#   1b) via 'bazel run' (similar to case 1a)
#   2) directly by a user, with $0 in X's runfiles tree
#   3) by another program Y which has a data dependency on X, with $0 in Y's runfiles tree
#   4) via 'bazel test'
#   5) by a genrule cmd, with $0 in the output tree
#   6) case 3 in the context of a genrule
#
# For case 1, $0 will be a regular file, and the runfiles tree will be
# at $0.runfiles.
# For case 2, $0 will be a symlink to the file seen in case 1.
# For case 3, we use Y's runfiles tree, which will be a superset of X's.
# For case 4, $JAVA_RUNFILES and $TEST_SRCDIR should already be set.
# Case 5 is handled like case 1.
# Case 6 is handled like case 3.

self="$0"

if [[ "$self" != /* ]]; then
  self="$PWD/$self"
fi

if [[ -z "$RUNFILES" ]]; then
while true; do
  if [[ -e "$self.runfiles" ]]; then
    RUNFILES="$self.runfiles"
    break
  fi
  if [[ $self == *.runfiles/* ]]; then
    RUNFILES="${{self%%.runfiles/*}}.runfiles"
    break
  fi
  if [[ ! -L "$self" ]]; then
    break
  fi
  readlink="$(readlink "$self")"
  if [[ "$readlink" = /* ]]; then
    self="$readlink"
  else
    # resolve relative symlink
    self="${{self%%/*}}/$readlink"
  fi
done
if [[ -z "$RUNFILES" ]]; then
  echo 'Cannot locate runfiles directory.'
  exit 1
fi
fi

# This is the end of the portion copied from the java_stub_template - what's below is original code.

BUILD_DIR="$BUILD_WORKSPACE_DIRECTORY"
if [ -n "$BUILD_DIR" ]; then
  BUILD_DIR="$BUILD_DIR/"
fi

TOOL={executable}
if [[ ! -f "$TOOL" ]]; then
  # The path to the tool contains starts with `/external` but when
  # using --nolegacy_external_runfiles we need to strip that from
  # the path.
  TOOL="$RUNFILES/${{TOOL/#external\\///}}"

  if [[ ! -f "$TOOL" ]]; then
    echo "Cannot locate linter. $TOOL"
    exit 2
  fi
fi

SRCS=({srcs})
SRCS=${{SRCS[@]/#/$BUILD_DIR}}

"$TOOL" {args} $SRCS
""".format(
        executable = ctx.executable._ktlint_tool.path,
        args = " ".join(args),
        srcs = " ".join([src.path for src in ctx.files.srcs]),
    )

    content = ctx.expand_location(content, [ctx.attr._ktlint_tool])

    executable = ctx.actions.declare_file("%s-lint-fix" % ctx.label.name)
    ctx.actions.write(
        output = executable,
        content = content,
        is_executable = True,
    )

    files = [ctx.executable._ktlint_tool]
    if editorconfig:
        files.append(editorconfig)
    runfiles = ctx.runfiles(files = files)

    return [
        DefaultInfo(
            executable = executable,
            runfiles = runfiles,
        ),
    ]

ktlint_fix = rule(
    _ktlint_fix_impl,
    attrs = {
        "srcs": attr.label_list(
            allow_files = [".kt", ".kts"],
            doc = "Source files to review and fix",
            mandatory = True,
            allow_empty = False,
        ),
        "config": attr.label(
            doc = "ktlint_config to use",
            providers = [
                [KtlintConfigInfo],
            ],
        ),
        "_ktlint_tool": attr.label(
            default = "@com_github_pinterest_ktlint//file",
            executable = True,
            cfg = "target",
        ),
    },
    executable = True,
    doc = "Lint Kotlin files and automatically fix them as needed",
)
